import { observe, extendObservable, makeAutoObservable } from 'mobx';
import { userAgent } from '@/common/utils';
import storage from '@/popup/IO/storage';
import SuiteState from './SuiteState';
import PlaybackState from './PlaybackState';
import ModalState from './ModalState';
import Command from '@/popup/models/Command';
import Manager from '@/popup/plugin/manager';
import WindowSession from '@/popup/IO/window-session';
import BackgroundRecorder from '@/popup/IO/SideeX/recorder';

class UiState {
    views = ['Tests', 'Test suites', 'Executing'];

    lastViewSelection = new Map();

    selectedView = 'Tests';

    selectedTest = {
        test: {
            id: 0,
            name: 'name',
            commands: [],
            export() {},
        },
    };

    selectedCommand = null;

    filterTerm = '';

    clipboard = null;

    isRecording = false;

    isBigSpacingEnabled = false;

    lastRecordedCommand = null;

    isSelectingTarget = false;

    windowHeight = window.innerHeight;

    consoleHeight = 200;

    minConsoleHeight = 30;

    minContentHeight = 460;

    minNavigationWidth = 180;

    maxNavigationWidth = 350;

    navigationWidth = 180;

    pristineCommand = new Command();

    lastFocus = {};

    options = {};

    pauseNotificationSent = false;

    isControlled = null;

    selectedExportLanguage = null;

    specifiedRemoteUrl = null;

    gridConfigEnabled = null;

    _keyboardShortcutsEnabled = true;

    dialogButtonDirection = 'normal';

    _project = {
        url: 'https://www.baidu.com/',
    };

    constructor() {
        this.suiteStates = {};
        this.filterFunction = this.filterFunction.bind(this);
        this.observePristine();
        storage.get().then((data) => {
            if (data.selectedExportLanguage !== undefined) {
                this.selectExportLanguage(data.selectedExportLanguage);
            } else {
                this.selectExportLanguage('java-junit');
            }
            this.specifyRemoteUrl(data.specifiedRemoteUrl ? data.specifiedRemoteUrl : 'http://localhost:4444/wd/hub');
            data.gridConfigEnabled ? (this.gridConfigEnabled = data.gridConfigEnabled) : (this.gridConfigEnabled = false);
            if (data.consoleSize !== undefined && data.consoleSize >= this.minConsoleHeight) {
                this.storedConsoleHeight =
                    data.consoleSize > this.minConsoleHeight ? data.consoleSize : this.windowHeight - this.minContentHeight;
                this.resizeConsole(data.consoleSize);
            }
            if (data.navigationSize !== undefined && data.navigationSize >= this.minNavigationWidth) {
                this.resizeNavigation(data.navigationSize);
            }
            if (data.options) {
                this.setOptions(data.options);
            }
        });
        this.recorder = new BackgroundRecorder(WindowSession);
        this.windowSession = WindowSession;
        // Dialog buttons are rendered with "action | cancel" layout on Windows
        // On other platforms default "cancel | action" layout is used
        this.dialogButtonDirection = userAgent.os.name === 'Windows' ? 'reversed' : 'normal';

        makeAutoObservable(this, {}, { autoBind: true });
    }

    setProject(project) {
        this._project = project;
    }

    toggleBigSpacingEnabled() {
        this.isBigSpacingEnabled = !this.isBigSpacingEnabled;
    }

    get project() {
        return this._project;
    }

    get filteredTests() {
        return this._project.tests.filter(this.filterFunction);
    }

    get baseUrl() {
        return this._project.url;
    }

    get maxContentHeight() {
        return this.windowHeight - this.minConsoleHeight;
    }

    get gaugeSpeed() {
        const value = PlaybackState.maxDelay - PlaybackState.delay;
        const speed = Math.ceil((value / PlaybackState.maxDelay) * 5);
        return speed ? speed : 1;
    }

    _changeView(view, ignoreCache) {
        this.lastViewSelection.set(this.selectedView, this.selectedTest);
        const lastSelection = this.lastViewSelection.get(view);
        if (!ignoreCache && lastSelection) {
            this.selectTest(lastSelection.test, lastSelection.suite, lastSelection.stack);
        }
        this.selectedView = view;
    }

    async changeView(view, ignoreCache) {
        if (this.isRecording && view !== this.selectedView) {
            const choseChange = await ModalState.showAlert({
                title: 'Stop recording',
                description: 'Changing the current view will stop the recording process. Would you like to continue?',
                confirmLabel: 'stop recording',
                cancelLabel: 'cancel',
            });
            if (choseChange) {
                await this.stopRecording();
                this._changeView(view, ignoreCache);
            }
        } else {
            this._changeView(view, ignoreCache);
        }
    }

    clearViewCache() {
        this.lastViewSelection.clear();
    }

    copyToClipboard(item) {
        this.clipboard = item;
    }

    pasteFromClipboard(index) {
        if (this.clipboard && this.displayedTest) {
            const newCommand = this.clipboard.clone();
            this.displayedTest.insertCommandAt(newCommand, index);
        }
    }

    get displayedTest() {
        return this.selectedTest.stack !== undefined &&
            this.selectedTest.stack >= 0 &&
            this.selectedTest.stack < PlaybackState.callstack.length
            ? PlaybackState.callstack[this.selectedTest.stack].callee
            : this.selectedTest.test;
    }

    get keyboardShortcutsEnabled() {
        return this._keyboardShortcutsEnabled;
    }

    _selectTest(test, suite, stack, override) {
        if (!PlaybackState.isPlaying || PlaybackState.paused || override) {
            const _test = stack !== undefined && stack >= 0 ? PlaybackState.callstack[stack].callee : test;
            if (_test && (_test !== this.displayedTest || suite !== this.selectedTest.suite)) {
                this.selectedTest = {
                    test,
                    suite,
                    stack: stack >= 0 ? stack : undefined,
                };
                if (PlaybackState.isPlaying && !PlaybackState.paused) {
                    this.selectCommand(undefined);
                } else if (_test && _test.commands.length) {
                    let command = this.selectedTest.test.selectedCommand;
                    command = command ? command : _test.commands[0];
                    this.selectCommand(command);
                } else if (_test && !_test.commands.length) {
                    this.selectCommand(this.pristineCommand);
                } else {
                    this.selectCommand(undefined);
                }
            } else if (!_test) {
                this.selectedTest = {};
                this.selectCommand(undefined);
            }
        }
    }

    async selectTest(test, suite, stack, override) {
        if (this.isRecording && test !== this.selectedTest.test) {
            const choseSelect = await ModalState.showAlert({
                title: 'Stop recording',
                description: 'Leaving this test and moving to another one will stop the recording process. Would you like to continue?',
                confirmLabel: 'stop recording',
                cancelLabel: 'cancel',
            });
            if (choseSelect) {
                await this.stopRecording();
                this._selectTest(test, suite, stack, override);
            }
        } else {
            this._selectTest(test, suite, stack, override);
        }
    }

    selectTestByIndex(index, suite) {
        const selectTestInArray = (index, tests) => (index >= 0 && index < tests.length ? tests[index] : undefined);
        if (this.selectedView === 'Tests') {
            const test = selectTestInArray(index, this.filteredTests);
            if (test) this.selectTest(test);
        } else if (this.selectedView === 'Test suites') {
            const suiteState = this.getSuiteState(suite);
            const tests = suiteState.filteredTests.get();
            const test = selectTestInArray(index, tests);
            const suiteIndex = this._project.suites.indexOf(suite);
            if (test) {
                suite.setOpen(true);
                this.selectTest(test, suite);
            } else if (suiteIndex > 0 && index < 0) {
                const previousSuite = this._project.suites[suiteIndex - 1];
                this.selectTestByIndex(this.getSuiteState(previousSuite).filteredTests.get().length - 1, previousSuite);
            } else if (suiteIndex + 1 < this._project.suites.length && index >= tests.length) {
                const nextSuite = this._project.suites[suiteIndex + 1];
                this.selectTestByIndex(0, nextSuite);
            }
        } else if (this.selectedView === 'Executing') {
            const test = selectTestInArray(index, PlaybackState.testsToRun);
            if (test) {
                let stack = undefined;
                if (PlaybackState.callstack.length && PlaybackState.stackCaller === test) {
                    stack = PlaybackState.callstack.length - 1;
                }
                this.selectTest(test, suite, stack);
            }
        }
    }

    selectCommand(command, opts = { isCommandTarget: false }) {
        if (!PlaybackState.isPlaying || PlaybackState.paused || opts.isCommandTarget) {
            if (this.selectedTest.test) {
                this.selectedTest.test.selectedCommand = command;
                this.selectedCommand = command;
            } else {
                this.selectedCommand = undefined;
            }
        }
    }

    selectCommandByIndex(index, opts) {
        const test = this.displayedTest;
        if (index >= 0 && index < test.commands.length) {
            this.selectCommand(test.commands[index], opts);
        } else if (index === test.commands.length) {
            this.selectCommand(this.pristineCommand, opts);
        }
    }

    selectNextCommand(opts = { from: undefined, isCommandTarget: false }) {
        this.selectCommandByIndex(this.nextCommandIndex(opts.from), opts);
    }

    nextCommandIndex(targetCommand) {
        const commands = this.displayedTest.commands;
        if (targetCommand) return commands.indexOf(targetCommand) + 1;
        else return commands.indexOf(this.selectedCommand) + 1;
    }

    changeFilter(term) {
        this.filterTerm = term;
    }

    async toggleRecord(isInvalid) {
        await (this.isRecording ? this.stopRecording() : this.startRecording(isInvalid));
    }

    beforeRecording() {}

    async startRecording(isInvalid) {
        let startingUrl = this.baseUrl;
        if (!startingUrl) {
            startingUrl = await ModalState.selectBaseUrl({
                isInvalid,
                confirmLabel: 'Start recording',
            });
        }
        try {
            await this.recorder.attach(startingUrl);
            this._setRecordingState(true);
            this.lastRecordedCommand = null;
            await this.emitRecordingState();
        } catch (err) {
            ModalState.showAlert({
                title: 'Could not start recording',
                description: err ? err.message : undefined,
            });
        }
    }

    nameNewTest(isEnabled = true) {
        const test = this.selectedTest.test;
        if (isEnabled && test.name === 'Untitled' && !test.nameDialogShown) {
            ModalState.renameTest(test.name, { isNewTest: true }).then((name) => {
                test.setName(name);
            });
            test.nameDialogShown = true;
        }
    }

    async stopRecording(opts = { nameNewTest: true }) {
        await this.recorder.detach();
        this._setRecordingState(false);
        await this.emitRecordingState();
        await this.nameNewTest(opts.nameNewTest);
    }

    // Do not call this method directly, use start and stop

    _setRecordingState(isRecording) {
        this.isRecording = isRecording;
    }

    emitRecordingState() {
        Manager.emitMessage({
            action: 'event',
            event: this.isRecording ? 'recordingStarted' : 'recordingStopped',
            options: {
                testName: this.selectedTest.test ? this.selectedTest.test.name : undefined,
            },
        });
    }

    setSelectingTarget(isSelecting) {
        this.isSelectingTarget = isSelecting;
    }

    selectExportLanguage(language) {
        this.selectedExportLanguage = language;
        storage.set({
            selectedExportLanguage: language,
        });
    }

    specifyRemoteUrl(url) {
        this.specifiedRemoteUrl = url;
        storage.set({
            specifiedRemoteUrl: url,
        });
    }

    toggleGridConfig() {
        this.gridConfigEnabled = !this.gridConfigEnabled;
        storage.set({
            gridConfigEnabled: this.gridConfigEnabled,
        });
    }

    resizeConsole(height) {
        const maxConsoleHeight = this.windowHeight - this.minContentHeight;
        const tmpHeight = height > maxConsoleHeight ? maxConsoleHeight : height;

        this.storedConsoleHeight = height > this.minConsoleHeight + 20 ? height : this.storedConsoleHeight;
        this.consoleHeight = height > this.minConsoleHeight ? tmpHeight : this.minConsoleHeight;

        storage.set({
            consoleSize: this.consoleHeight,
        });
    }

    maximizeConsole() {
        this.resizeConsole(this.windowHeight - this.minContentHeight);
    }

    minimizeConsole() {
        this.resizeConsole(this.minConsoleHeight);
    }

    restoreConsoleSize() {
        this.resizeConsole(this.storedConsoleHeight);
    }

    toggleConsole() {
        if (this.consoleHeight === this.minConsoleHeight) {
            this.restoreConsoleSize();
        } else {
            this.minimizeConsole();
        }
    }

    setWindowHeight(height) {
        this.windowHeight = height;
        if (this.windowHeight - this.consoleHeight < this.minContentHeight) {
            this.resizeConsole(this.windowHeight - this.minContentHeight);
        }
    }

    resizeNavigation(width) {
        this.navigationWidth = width;
        storage.set({
            navigationSize: this.navigationWidth,
        });
    }

    setOptions(options) {
        extendObservable(this.options, options);
        storage.set({
            options: this.options,
        });
    }

    observePristine() {
        this.pristineDisposer = observe(this.pristineCommand, () => {
            this.pristineDisposer();
            this.displayedTest.addCommand(this.pristineCommand);
            this.pristineCommand = new Command();
            this.observePristine();
        });
    }

    focusNavigation() {
        if (this.lastFocus.navigation) {
            this.lastFocus.navigation();
        }
    }

    focusEditor() {
        if (this.lastFocus.editor) {
            this.lastFocus.editor();
        }
    }

    setSectionFocus(section, cb) {
        this.lastFocus[section] = cb;
    }

    getSuiteState(suite) {
        if (!this.suiteStates[suite.id]) {
            this.suiteStates[suite.id] = new SuiteState(this, suite);
        }

        return this.suiteStates[suite.id];
    }

    filterFunction({ name }) {
        return name.toLowerCase().indexOf(this.filterTerm.toLowerCase()) !== -1;
    }

    setUrl(url, addToCache) {
        this._project.setUrl(url);
        if (addToCache) this._project.addUrl(url);
    }

    projectChanged() {
        this.selectedTest = {};
        this.selectedCommand = null;
        this.filterTerm = '';
        this.clipboard = null;
        this.isRecording = false;
        this.suiteStates = {};
        this.selectTest(this._project.tests[0]);
        WindowSession.closeAllOpenedWindows();
        this.saved();
    }

    isSaved() {
        return this._project.modified === false;
    }

    saved() {
        this._project.saved();
    }

    startConnection() {
        this.isControlled = true;
    }

    toggleKeyboardShortcuts() {
        this._keyboardShortcutsEnabled = !this._keyboardShortcutsEnabled;
    }
}

if (!window._state) window._state = new UiState();

export default window._state;
